package org.jfsm.core;

import java.lang.reflect.Constructor;
import java.lang.reflect.Field;

public class FSMInput<C extends FSMContext> extends SignalGroup {

    public final class Signal extends RetSignal {

        public final void dispatch(C destCtx) {
            dispatcher.dispatch(destCtx, eid, null);
        }

        Object getMsgObject() {
            return null;
        }
    }

    public final class SignalM<M> extends FSMSignalM<M> {

        public final class Returnable extends RetSignal {

            Object msg;

            Object getMsgObject() {
                return msg;
            }

            Returnable(Object msg) {
                this.msg = msg;
                eid = SignalM.this.eid;
            }

        }

        SignalM(Field f) {
            super(f);
        }

        public final void dispatch(C destCtx, M msg) {
            dispatcher.dispatch(destCtx, eid, msg);
        }

        public final RetSignal asNextSignal(M msg) {
            return new Returnable(msg);
        }
    }

    public final class SignalS<M> extends FSMSignalS<M> {

        SignalS(Field f) {
            super(f);
        }

        public final void dispatch(C destCtx, FSMContext ctx, M msg) {
            dispatcher.dispatch(destCtx, FSMWorker.SESSION_SIGNAL, new SessionMessage(
                    new FSMReplyContext(ctx, refID), getEid(), msg));
        }

    }

    private Dispatcher dispatcher = new Dispatcher(0);
    private Constructor<C> ctxConstructor;
    int refID;

    public final C newContext() {
        try {
            C ctx = ctxConstructor.newInstance();
            dispatcher.initContext(ctx);
            return ctx;

        } catch (Exception e) {
            return null;
        }
    }

    @Override
    FSMSignal newSignal(Field f) {
        return new Signal();
    }

    @SuppressWarnings("unchecked")
    @Override
    FSMSignal newSignalM(Field f) {
        return new SignalM(f);
    }

    @SuppressWarnings("unchecked")
    @Override
    FSMSignal newSignalS(Field f) {
        return new SignalS(f);
    }

    @SuppressWarnings("unchecked")
    void setProxy(FSMInput<?> proxy) {
        dispatcher.worker = proxy.dispatcher.worker;
        ctxConstructor = (Constructor<C>) proxy.ctxConstructor;
    }

    void setWorker(FSMWorker worker) {
        dispatcher.worker = worker;
    }

    void setClassC(Class<C> classC) throws Exception {
        ctxConstructor = classC.getDeclaredConstructor();
        ctxConstructor.setAccessible(true);
    }
}
